package com.acme.gprs.server;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.zip.Adler32;
import java.util.zip.CheckedInputStream;

final class GPRSMessage {
	public static final String DIRECTION_SERVER_TO_TRACKER = "@@";
	public static final String DIRECTION_TRACKER_TO_SERVER = "$$";
	public static final String FIELDS_SEPARATOR = ",";
	public static final String CHECKSUM_SEPARATOR = "*";
	public static final String ENDING_CHARACTER = "\r\n";
	
	public static final int ALERT_OPEN_DOOR = 1;
	public static final int ALERT_CLOSE_DOOR = 2;
	public static final int ALERT_POWER_OFF = 3;
	public static final int ALERT_POWER_ON = 4;
	
	private String direction;
	private int packageFlag;
	private int length;
	private String imei;
	private String commandCode;
	private String[] data;
	private long checksum;
	
	public GPRSMessage(String direction, int packageFlag, int length,
			String imei, String commandCode, String[] data, int checksum) {
		this.direction = direction;
		this.packageFlag = packageFlag;
		this.length = length;
		this.imei = imei;
		this.commandCode = commandCode;
		this.data = data;
		this.checksum = checksum;
	}
	
	public GPRSMessage(char packageFlag, String imei, String commandCode, String[] data) {
		this.direction = DIRECTION_SERVER_TO_TRACKER;
		this.packageFlag = packageFlag;
		this.imei = imei;
		this.commandCode = commandCode;
		this.data = data;
	}

	public static GPRSMessage parseStrMessage(String strMessage) throws Exception{
		GPRSMessage msg = null;
		if(strMessage != null && !strMessage.isEmpty()){
			String[] commandParts = strMessage.trim().split("\\"+CHECKSUM_SEPARATOR);
			String[] parts = commandParts[0].split(FIELDS_SEPARATOR, -1);
			String[] data = Arrays.copyOfRange(parts,3,parts.length);
			int checksum = Integer.valueOf(commandParts[1],16);
			msg = new GPRSMessage(
					parts[0].substring(0,2),
					parts[0].substring(2,3).codePointAt(0),
					Integer.parseInt(parts[0].substring(3,parts[0].length())),
					parts[1],
					parts[2],
					data,
					checksum
			);
		}
		return msg;
	}
	
	
	public String getDirection() {
		return direction;
	}

	public int getPackageFlag() {
		return packageFlag;
	}

	public int getLength() {
		return length;
	}

	public String getImei() {
		return imei;
	}

	public String getCommandCode() {
		return commandCode;
	}

	public String[] getData() {
		return data;
	}

	public long getChecksum() {
		return checksum;
	}
	
	public long checksum(byte [] bytes)throws IOException{
		CheckedInputStream cis = new CheckedInputStream(new ByteArrayInputStream(bytes), new Adler32());
		cis.read(new byte[bytes.length]);
		long value = cis.getChecksum().getValue();
		return (value-1) & 0x000000FF;
	}

	@Override
	public String toString() {
		return  "<dir>: " + direction + "|" + 
				"<package flag>: " + new Character((char)packageFlag) + "|" +
				"<L>: " + length + "|" +
				"<IMEI>: " + imei + "|" +
				"<command>: " + commandCode + "|" +
				"<data>: " + Arrays.toString(data) + "|" +
				"<*checksum>: " + (Long.toHexString(checksum).length()==1?"0":"") + Long.toHexString(checksum).toUpperCase();
	}
	
	public String toCanonicalRepr() throws IOException{
		String trailingPart = 
				FIELDS_SEPARATOR + imei + FIELDS_SEPARATOR +
				commandCode + ((data!=null && data.length>0)?FIELDS_SEPARATOR:"") + 
				((data!=null && data.length>0) ? Arrays.toString(data).replace("[", "").replace("]", "").replace(" ", "") : "" ) +
				CHECKSUM_SEPARATOR;
		length = trailingPart.length() + 4;
		String leadingPart = 
				direction +
				new Character((char)packageFlag) + 
				length;
		checksum = checksum(new String(leadingPart + trailingPart).getBytes());
		return leadingPart + trailingPart + (Long.toHexString(checksum).length()==1?"0":"") + Long.toHexString(checksum).toUpperCase() + ENDING_CHARACTER;
	}
}
